// $Id: pstring.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <cstring>
#include <boost/test/unit_test.hpp>
#include <xpcom/memory.hpp>
#include <xpcom/string.hpp>
#include <xpcom/impl/pstring.hpp>

namespace xpcom {
namespace {

const char* s_s1 = "test1";
const char* s_s2 = "test2";
const char* s_s3 = "test3";
const char* s_ss = "";

void do_out_impl_1(string& s)
{
    BOOST_CHECK(s.empty());
    s.clear();
}

void do_out_1(char** s)
{
    do_out_impl_1(impl::pstr_out(s));
}

void do_out_size_1(char** s, uint32_t* n, uint32_t* l)
{
    do_out_impl_1(impl::pstr_out_size(s, n, l));
}

void do_out_size2_1(char** s, uint32_t* n)
{
    do_out_impl_1(impl::pstr_out_size2(s, n));
}

void do_out_impl_2(string& s)
{
    BOOST_CHECK(s.empty());
    s = s_s1;
}

void do_out_2(char** s)
{
    do_out_impl_2(impl::pstr_out(s));
}

void do_out_size_2(char** s, uint32_t* n, uint32_t* l)
{
    do_out_impl_2(impl::pstr_out_size(s, n, l));
}

void do_out_size2_2(char** s, uint32_t* n)
{
    do_out_impl_2(impl::pstr_out_size2(s, n));
}

void do_out_impl_3(string& s)
{
    BOOST_CHECK(s.empty());
    s = s_ss;
}

void do_out_3(char** s)
{
    do_out_impl_3(impl::pstr_out(s));
}

void do_out_size_3(char** s, uint32_t* n, uint32_t* l)
{
    do_out_impl_3(impl::pstr_out_size(s, n, l));
}

void do_out_size2_3(char** s, uint32_t* n)
{
    do_out_impl_3(impl::pstr_out_size2(s, n));
}

string do_out_impl_retval()
{
    string s = s_s3;
    return s;
}

void do_out_retval(char** s)
{
    impl::pstr_out retval(s);
    retval = do_out_impl_retval();
}

void do_out_size_retval(char** s, uint32_t* n, uint32_t* l)
{
    impl::pstr_out_size retval(s, n, l);
    retval = do_out_impl_retval();
}

void do_out_size2_retval(char** s, uint32_t* n)
{
    impl::pstr_out_size2 retval(s, n);
    retval = do_out_impl_retval();
}

void do_inout_impl_1(string& s)
{
    BOOST_CHECK(s.empty());
    s = s_s1;
}

void do_inout_impl_size_1(string& s)
{
    BOOST_CHECK(s.empty());
    s = s_s1;
    s.resize(10, 'x');
}

void do_inout_1(char** s)
{
    do_inout_impl_1(impl::pstr_inout(s));
}

void do_inout_size_1(char** s, uint32_t* n, uint32_t* l)
{
    do_inout_impl_size_1(impl::pstr_inout_size(s, n, l));
}

void do_inout_size2_1(char** s, uint32_t* n)
{
    do_inout_impl_size_1(impl::pstr_inout_size2(s, n));
}

void do_inout_impl_2(string& s)
{
    BOOST_CHECK(!s.empty());
    BOOST_CHECK(s == s_s2);

    s = s_s3;
}

void do_inout_impl_size_2(string& s)
{
    BOOST_CHECK(!s.empty());
    BOOST_CHECK(s == "test");

    s = s_s3;
    s.resize(3);
}

void do_inout_2(char** s)
{
    do_inout_impl_2(impl::pstr_inout(s));
}

void do_inout_size_2(char** s, uint32_t* n, uint32_t* l)
{
    do_inout_impl_size_2(impl::pstr_inout_size(s, n, l));
}

void do_inout_size2_2(char** s, uint32_t* n)
{
    do_inout_impl_size_2(impl::pstr_inout_size2(s, n));
}

void do_inout_impl_3(string& s)
{
    BOOST_CHECK(!s.empty());
    BOOST_CHECK(s == s_s1);

    s.clear();
}

void do_inout_3(char** s)
{
    do_inout_impl_3(impl::pstr_inout(s));
}

void do_inout_size_3(char** s, uint32_t* n, uint32_t* l)
{
    do_inout_impl_3(impl::pstr_inout_size(s, n, l));
}

void do_inout_size2_3(char** s, uint32_t* n)
{
    do_inout_impl_3(impl::pstr_inout_size2(s, n));
}

void do_out_isize2_impl_1(string& s, uint32_t n)
{
    BOOST_CHECK(s.empty());
    s.resize(n, 'x');
}

void do_out_isize2_1(char** s, uint32_t n)
{
    do_out_isize2_impl_1(impl::pstr_out_isize2(s, n), n);
}

void do_out_isize2_impl_2(string& s, uint32_t n)
{
    BOOST_CHECK(s.empty());
    s.resize(n - 1, 'x'); // to produce less data, cause error
}

void do_out_isize2_2(char** s, uint32_t n)
{
    do_out_isize2_impl_2(impl::pstr_out_isize2(s, n), n);
}

string do_out_isize2_impl_retval(uint32_t n)
{
    string s;
    s.resize(n, 'x');
    return s;
}

void do_out_isize2_retval(char** s, uint32_t n)
{
    impl::pstr_out_isize2 retval(s, n);
    retval = do_out_isize2_impl_retval(n);
}

}
}

BOOST_AUTO_TEST_CASE(test_pstr_in)
{
    using namespace xpcom;

    const char* s1 = s_s1;
    const char* s2 = 0;

    BOOST_CHECK(impl::pstr_in(s1) == s_s1);
    BOOST_CHECK(impl::pstr_in(s2) == 0);
}

BOOST_AUTO_TEST_CASE(test_pstr_out)
{
    using namespace xpcom;

    char* s;

    do_out_1(&s);
    BOOST_CHECK(s == 0);

    do_out_2(&s);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s1) == 0);
    memory::free(s);

    do_out_3(&s);
    BOOST_CHECK(s == 0);

    do_out_retval(&s);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s3) == 0);
    memory::free(s);
}

BOOST_AUTO_TEST_CASE(test_pstr_out_size)
{
    using namespace xpcom;

    char* s;
    uint32_t size, len;

    do_out_size_1(&s, &size, &len);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);
    BOOST_CHECK_EQUAL(len, 0);

    do_out_size_2(&s, &size, &len);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s1) == 0);
    BOOST_CHECK_EQUAL(size, 5);
    BOOST_CHECK_EQUAL(len, 5);
    memory::free(s);

    do_out_size_3(&s, &size, &len);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);
    BOOST_CHECK_EQUAL(len, 0);

    do_out_size_retval(&s, &size, &len);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s3) == 0);
    BOOST_CHECK_EQUAL(size, 5);
    BOOST_CHECK_EQUAL(len, 5);
    memory::free(s);
}

BOOST_AUTO_TEST_CASE(test_pstr_out_size2)
{
    using namespace xpcom;

    char* s;
    uint32_t size;

    do_out_size2_1(&s, &size);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);

    do_out_size2_2(&s, &size);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s1) == 0);
    BOOST_CHECK_EQUAL(size, 5);
    memory::free(s);

    do_out_size2_3(&s, &size);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);

    do_out_size2_retval(&s, &size);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s3) == 0);
    BOOST_CHECK_EQUAL(size, 5);
    memory::free(s);
}

BOOST_AUTO_TEST_CASE(test_pstr_inout)
{
    using namespace xpcom;

    char* s = 0;

    do_inout_1(&s);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s1) == 0);
    memory::free(s);

    s = memory::clone(s_s2, 6);
    do_inout_2(&s);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, s_s3) == 0);
    memory::free(s);

    s = memory::clone(s_s1, 6);
    do_inout_3(&s);
    BOOST_CHECK(s == 0);
}

BOOST_AUTO_TEST_CASE(test_pstr_inout_size)
{
    using namespace xpcom;

    char* s = 0;
    uint32_t size = 0, len = 0;

    do_inout_size_1(&s, &size, &len);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "test1xxxxx") == 0);
    BOOST_CHECK_EQUAL(size, 10);
    BOOST_CHECK_EQUAL(len, 10);
    memory::free(s);

    s = memory::clone(s_s2, 6);
    size = 5;
    len = 4;
    do_inout_size_2(&s, &size, &len);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "tes") == 0);
    BOOST_CHECK_EQUAL(size, 3);
    BOOST_CHECK_EQUAL(len, 3);
    memory::free(s);

    s = memory::clone(s_s1, 6);
    size = 5;
    len = 5;
    do_inout_size_3(&s, &size, &len);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);
    BOOST_CHECK_EQUAL(len, 0);
}

BOOST_AUTO_TEST_CASE(test_pstr_inout_size2)
{
    using namespace xpcom;

    char* s = 0;
    uint32_t size = 0;

    do_inout_size2_1(&s, &size);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "test1xxxxx") == 0);
    BOOST_CHECK_EQUAL(size, 10);
    memory::free(s);

    s = memory::clone(s_s2, 6);
    size = 4;
    do_inout_size2_2(&s, &size);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "tes") == 0);
    BOOST_CHECK_EQUAL(size, 3);
    memory::free(s);

    s = memory::clone(s_s1, 6);
    size = 5;
    do_inout_size2_3(&s, &size);
    BOOST_CHECK(s == 0);
    BOOST_CHECK_EQUAL(size, 0);
}

BOOST_AUTO_TEST_CASE(test_pstr_out_isize2)
{
    using namespace xpcom;

    char* s;

    do_out_isize2_1(&s, 0);
    BOOST_CHECK(s == 0);

    do_out_isize2_1(&s, 5);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "xxxxx") == 0);
    memory::free(s);

#ifdef NDEBUG // to avoid assertion failure
    do_out_isize2_2(&s, 5); // returned length less than requested
    BOOST_CHECK(s == 0);
#endif

    do_out_isize2_retval(&s, 8);
    BOOST_CHECK(s != 0);
    BOOST_CHECK(std::strcmp(s, "xxxxxxxx") == 0);
    memory::free(s);
}
